#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <assert.h>

#include "socket_server.h"
#include "socket_epoll.h"

#define MAX_SOCKET_P 16
#define MAX_SOCKET (1<<MAX_SOCKET_P)
#define HASH_ID(id) (((uint32_t)id) % MAX_SOCKET)

#define MAX_EVENT 64

#define MIN_READ_BUFFER 64

#define SOCKET_TYPE_INVALID 0
#define SOCKET_TYPE_RESERVE 1
#define SOCKET_TYPE_PLISTEN 2
#define SOCKET_TYPE_LISTEN 3
#define SOCKET_TYPE_CONNECTING 4
#define SOCKET_TYPE_CONNECTED 5
#define SOCKET_TYPE_HALFCLOSE 6
#define SOCKET_TYPE_PACCEPT 7
#define SOCKET_TYPE_BIND 8

struct write_buffer {
    struct write_buffer *next;
    void *buffer;
    int sz;
};
struct wb_list {
    struct write_buffer *head;
    struct write_buffer *tail;
};

struct socket_message {};
struct socket {
    struct wb_list high;
    struct wb_list low;
    int64_t wb_size;
    int fd;
    int id;
    uint16_t type;
    int rd_size;
};
struct socket_server {
    int event_fd;
    struct socket slot[MAX_SOCKET];
    int alloc_id;
    struct event ev[MAX_EVENT]; //epoll_wait
    int event_n;
    int event_index;
};

static void clear_wb_list(struct wb_list *list) {
    list->head = NULL;
    list->tail = NULL;
}

//--create and release
struct socket_server *socket_server_create() {
    int efd = sp_create();
    if (sp_invalid(efd)) {
        fprintf(stderr, "socket-server: create event poll failed.\n");
        return NULL;
    }

    struct socket_server *ss = (struct socket_server*)malloc(sizeof(*ss));
    ss->event_fd = efd;
    for (int i = 0; i < MAX_SOCKET; ++i) {
        struct socket *s = &ss->slot[i];
        s->type = SOCKET_TYPE_INVALID;
        clear_wb_list(&s->high);
        clear_wb_list(&s->low);
    }
    ss->alloc_id = 0;
    ss->event_n = 0;
    ss->event_index = 0;
}

static void write_buffer_free(struct socket_server *ss, struct write_buffer *wb) {
    free(wb->buffer);
    free(wb);
}

static void free_wb_list(struct socket_server *ss, struct wb_list *list) {
    struct write_buffer *wb = list->head;
    while (wb) {
        struct write_buffer *tmp = wb;
        wb = wb->next;
        write_buffer_free(ss, tmp);
    }
    list->head = NULL;
    list->tail = NULL;
}

static void force_close(struct socket_server *ss, struct socket *s) {
    if (SOCKET_TYPE_INVALID == s->type) {
        return;
    }
    free_wb_list(ss, &s->high);
    free_wb_list(ss, &s->low);
    sp_del(ss->event_fd, s->fd);
    close(s->fd);
    s->type = SOCKET_TYPE_INVALID;
}

void socket_server_release(struct socket_server *ss) {
    for (int i = 0; i < MAX_SOCKET; ++i) {
        struct socket *s = &ss->slot[i];
        force_close(ss, s);
    }
    sp_release(ss->event_fd);
    free(ss);
}
// ---------

//---bind-listen
static int do_bind(const char *host, int port) {
    if (NULL == host || 0 == host[0]) {
        host = "0.0.0.0";
    }
    char portstr[16];
    sprintf(portstr, "%d", port);

    struct addrinfo ai_hints;
    struct addrinfo *ai_list = NULL;
    memset(&ai_hints, 0, sizeof(ai_hints));
    ai_hints.ai_family = AF_UNSPEC;
    ai_hints.ai_socktype = SOCK_STREAM;
    ai_hints.ai_protocol = IPPROTO_TCP;
    int status = getaddrinfo(host, portstr, &ai_hints, &ai_list);
    if (0 != status) {
        return -1;
    }

    int resue = 1;
    int fd = socket(ai_list->ai_family, ai_list->ai_socktype, 0);
    if (fd < 0) {
        goto _failed_fd;
    }
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&resue, -1==sizeof(int))) {
        goto _failed;
    }
    status = bind(fd, (struct sockaddr*)ai_list->ai_addr, ai_list->ai_addrlen);
    if (0 != status) {
        goto _failed;
    }

    freeaddrinfo(ai_list);
    return fd;

_failed:
    close(fd);
_failed_fd:
    freeaddrinfo(ai_list);
    return -1;
}
static int do_listen(const char *host, int port, int backlog) {
    int listen_fd = do_bind(host, port);
    if (listen_fd < 0) {
        return -1;
    }
    if (-1 == listen(listen_fd, backlog)) {
        close(listen_fd);
        return -1;
    }
    return listen_fd;
}
static inline void check_wb_list(struct wb_list *s) {
    assert(s->head == NULL);
    assert(s->tail == NULL);
}
static struct socket *new_fd(struct socket_server *ss, int id, int fd) {
    struct socket *s = &ss->slot[HASH_ID(id)];
    assert(s->type == SOCKET_TYPE_RESERVE);
    if (sp_add(ss->event_fd, fd, s)) {
        s->type = SOCKET_TYPE_INVALID;
        return NULL;
    }
    s->id = id;
    s->fd = fd;
    s->rd_size = MIN_READ_BUFFER;
    s->wb_size = 0;
    check_wb_list(&s->high);
    check_wb_list(&s->low);
    return s;
}
static int reserve_id(struct socket_server *ss) {
    for (int i = 0; i < MAX_SOCKET; ++i) {
        int id = ++ss->alloc_id;
        if (id < 0) {
            id = ss->alloc_id + 0x7fffffff;
        }
        struct socket *s = &ss->slot[HASH_ID(id)];
        if (SOCKET_TYPE_INVALID == s->type) {
            s->type = SOCKET_TYPE_RESERVE;
            s->id = id;
            s->fd = -1;
            return id;
        } else {
            --i;
        }
    }
    return -1;
}

int socket_server_listen(struct socket_server *ss, const char *addr, int port, int backlog) {
    int listen_fd = do_listen(addr, port, backlog);
    if (listen_fd < 0) {
        return -1;
    }
    int id = reserve_id(ss);
    if (id < 0) {
        close(listen_fd);
        return id;
    }
    struct socket *s = new_fd(ss, id, listen_fd);
    if (NULL == s) {
        goto _failed;
    }
    s->type = SOCKET_TYPE_LISTEN;
    return 0;
_failed:
    close(listen_fd);
    ss->slot[HASH_ID(id)].type = SOCKET_TYPE_INVALID;
    return -1;
}
//----------------

//accept
static void socket_keepalive(int fd) {
    int keepalive = 1;
    setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive, sizeof(keepalive));
}
static int do_accept(struct socket_server *ss, struct socket *s) {
    struct sockaddr_in sa;
    socklen_t len = sizeof(s);
    int client_fd = accept(s->fd, (struct sockaddr*)&sa, &len);
    if (client_fd < 0) {
        return -1;
    }
    int id = reserve_id(ss);
    if (id < 0) {
        close(client_fd);
        return -1;
    }
    socket_keepalive(client_fd);
    sp_nonblocking(client_fd);
    struct socket *ns = new_fd(ss, id, client_fd);
    ns->type = SOCKET_TYPE_CONNECTED;

    void *sin_addr = (void*)&sa.sin_addr;
    int sin_port = sa.sin_port;
    char tmp[INET_ADDRSTRLEN];
    if (inet_ntop(sa.sin_family, sin_addr, tmp, sizeof(tmp))) {
        fprintf(stdout, "accept from %s:%d", tmp, sin_port);
    }
    return 0;
}
//--------------

//-----connect----
int client_connect(struct socket_server *ss, const char *host, int iport) {
    int id = reserve_id(ss);
    char port[16];
    sprintf(port, "%d", iport);
    struct addrinfo ai_hints;
    memset(&ai_hints, 0, sizeof(ai_hints));
    ai_hints.ai_family = AF_UNSPEC;
    ai_hints.ai_socktype = SOCK_STREAM;
    ai_hints.ai_protocol = IPPROTO_TCP;
    struct addrinfo *ai_list = NULL;
    struct addrinfo *ai_ptr = NULL;
    int sock = -1;
    struct socket *cs = new_fd(ss, id, sock);
    int status = getaddrinfo(host, port, &ai_hints, &ai_list);
    if (status != 0) {
        goto _failed;
    }
    
    for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) {
        sock = socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
        if (sock < 0) {
            continue;
        }
        socket_keepalive(sock);
        sp_nonblocking(sock);
        status = connect(sock, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
        if (status != 0 && errno != EINPROGRESS) {
            close(sock);
            sock = -1;
            continue;
        }
        break;
    }
    if (sock < 0) {
        goto _failed;
    }

    if (NULL == cs) {
        close(sock);
        goto _failed;
    }
    if (0 == status) {
        cs->type = SOCKET_TYPE_CONNECTED;  
    } else {
        cs->type = SOCKET_TYPE_CONNECTING;
        sp_write(ss->event_fd, cs->fd, cs, true);
    }
    freeaddrinfo(ai_list);
    return 0;

_failed:
    freeaddrinfo(ai_list);
    ss->slot[HASH_ID(id)].type = SOCKET_TYPE_INVALID;
    return -1;
}
static int send_buffer_empty(struct socket *s) {
    return (s->high.head == NULL && s->low.head == NULL);
}
int do_connecting(struct socket_server *ss, struct socket *s) {
    int error;
    socklen_t len = sizeof(error);
    int code = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &error, &len);
    if (code < 0 || error) {
        force_close(ss, s);
        return -1;
    } else {
        s->type = SOCKET_TYPE_CONNECTED;
        if (send_buffer_empty(s)) {
            sp_write(ss->event_fd, s->fd, s, false);
        }
        struct sockaddr_in si;
        socklen_t slen = sizeof(si);
        if (0 == getpeername(s->fd, (struct sockaddr*)&si, &slen)) {
            void *sin_addr = (void*)&si.sin_addr;
            char tmp[INET_ADDRSTRLEN];
            inet_ntop(si.sin_family, sin_addr, tmp, sizeof(tmp));
            fprintf(stdout, "%s", tmp);
        }
        return 0;
    }
}
//----------------

//msg
static int read_msg(struct socket_server *ss, struct socket *s) {
    int sz = s->rd_size;
    char *buffer = (char *)malloc(sz);
    int n = (int)read(s->fd, buffer, sz);
    if (n < 0) {
        free(buffer);
        switch(errno) {
            case EINTR:
                break;
            case EAGAIN:
                fprintf(stderr, "socket-server: EAGAIN capture.\n");
                break;
            default:
                force_close(ss, s);
                return -1;
        }
        return 0;
    }
    if (0 == n) {
        free(buffer);
        force_close(ss, s);
        return -1;
    }

    if (s->type == SOCKET_TYPE_HALFCLOSE) {
        free(buffer);
        return 0;
    }
    if (n == sz) {
        s->rd_size *= 2;
    } else if (sz > MIN_READ_BUFFER && n * 2 < sz) {
        s->rd_size /= 2;
    }
    return 0;
}
static int send_buffer(struct socket_server *ss, struct socket *s) {
    
}
//----------

//event-loop
int socket_server_poll(struct socket_server *ss) {
    for(;;) {
        if (ss->event_index == ss->event_n) {
            ss->event_n = sp_wait(ss->event_fd, ss->ev, MAX_EVENT);
            ss->event_index = 0;
            if (ss->event_n <= 0) {
                ss->event_n = 0;
                return -1;
            }
        }
        struct event *e = &ss->ev[ss->event_index++];
        struct socket *s = (struct socket*)e->s;
        if (NULL == s) {
            continue;
        }
        switch(s->type) {
            case SOCKET_TYPE_LISTEN:
                do_accept(ss, s);
                break;
            case SOCKET_TYPE_CONNECTING:
                do_connecting(ss, s);
                break;
            case SOCKET_TYPE_CONNECTED:
                if (e->read) {
                   read_msg(ss, s);
                }
                if (e->write) {
                    send_buffer(ss, s);
                }
                break;    
            default:
                break;
        }
    }
}
//-----------